package tcode

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"unsafe"
)

type rawTable struct {
	data        [][]any
	columnNames []string
}

func (r *rawTable) RawData() [][]any {
	return r.data
}

func (r *rawTable) ColumnNames() []string {
	return r.columnNames
}

func (r *rawTable) RowCount() int {
	return len(r.data)
}
func (r *rawTable) ColumnCount() int {
	return len(r.columnNames)
}

func (r *rawTable) GetRow(rIndex int) map[string]string {
	rd := make(map[string]string)
	if rIndex > -1 && rIndex < r.RowCount() {
		for cIndex := range r.columnNames {
			rd[r.columnNames[cIndex]] = ConvertToString(r.data[rIndex][cIndex])
		}
		return rd
	}
	return rd
}

func (r *rawTable) GetColumnByIndex(cIndex int) []string {
	var cols []string
	cCount := r.ColumnCount()
	if cIndex > -1 && cIndex < cCount {
		for rIndex := 0; rIndex < cCount; rIndex++ {
			cols = append(cols, ConvertToString(r.data[rIndex][cIndex]))
		}
	}
	return cols
}

func (r *rawTable) GetColumnByName(colName string) []string {
	var cols []string
	cIndex := StringInIndex(colName, r.columnNames)
	cCount := r.ColumnCount()
	if cIndex > -1 && cIndex < cCount {
		for rIndex := 0; rIndex < cCount; rIndex++ {
			cols = append(cols, ConvertToString(r.data[rIndex][cIndex]))
		}
	}
	return cols
}

func (r *rawTable) TableName() string {
	return ""
}

func (r *rawTable) RawColumnContainer(columns ...string) []any {
	dest := make([]any, len(columns))
	for i := range columns {
		dest[i] = new(any)
	}
	r.data = append(r.data, dest)
	return dest
}

func (r *rawTable) NewTable() Table {
	return &rawTable{
		data:        r.data,
		columnNames: r.columnNames,
	}
}

func (r *rawTable) NewInstance() Table {
	return &rawTable{}
}

func (r *rawTable) CopyFrom(src Table) {
	if source, ok := src.(*rawTable); ok {
		r.data = source.data
		r.columnNames = source.columnNames
	}
}

func (r *rawTable) Columns() []string {
	return r.columnNames
}

func setValue(field *reflect.Value, value any) (err error) {
	if value == nil {
		return err
	}
	defer func() {
		var ok bool
		r := recover()
		if r == nil {
			return
		}
		if err, ok = r.(error); !ok {
			panic(r)
		}
	}()
	if !field.CanSet() {
		return err
	}
	switch field.Kind() {
	case reflect.Invalid:
		return err
	case reflect.Bool:
		switch v := value.(type) {
		case bool:
			field.SetBool(v)
		default:
			parseBool, err := strconv.ParseBool(ConvertToString(value))
			if err != nil {
				return err
			}
			field.SetBool(parseBool)
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		switch v := value.(type) {
		case []byte:
			parseInt, err := strconv.ParseInt(string(v), 10, 64)
			if err != nil {
				return err
			}
			field.SetInt(parseInt)
		default:
			field.SetInt(value.(int64))
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		switch v := value.(type) {
		case []byte:
			parseInt, err := strconv.ParseUint(string(v), 10, 64)
			if err != nil {
				return err
			}
			field.SetUint(parseInt)
		default:
			field.SetUint(value.(uint64))
		}
	case reflect.Uintptr:
		field.Set(reflect.ValueOf(value.(uintptr)))
	case reflect.Float32:
		switch v := value.(type) {
		case []byte:
			parseFloat, err := strconv.ParseFloat(string(v), 10)
			if err != nil {
				return err
			}
			field.SetFloat(parseFloat)
		default:
			field.SetFloat(float64(value.(float32)))
		}
	case reflect.Float64:
		switch v := value.(type) {
		case []byte:
			parseFloat, err := strconv.ParseFloat(string(v), 10)
			if err != nil {
				return err
			}
			field.SetFloat(parseFloat)
		default:
			field.SetFloat(value.(float64))
		}
	case reflect.Complex64, reflect.Complex128:
		field.SetComplex(value.(complex128))
	case reflect.Array:
		// TODO
	case reflect.Chan:
		// TODO
	case reflect.Func:
		// TODO
	case reflect.Interface:
		// TODO
	case reflect.Map:
		// TODO
	case reflect.Pointer:
		// TODO
	case reflect.Slice:
		// TODO
	case reflect.String:
		field.SetString(ConvertToString(value))
	case reflect.Struct:
		field.Set(reflect.ValueOf(value))
	case reflect.UnsafePointer:
		field.SetPointer(value.(unsafe.Pointer))
	default:
		field.Set(reflect.ValueOf(value))
	}
	return err
}

// Convert 转换为结构体或切片  可转换的类型(*struct ,*[]struct, *[]*struct, *[]基本类型, *基本类型)
func Convert(r *rawTable, ptr any) (err error) {
	return convert(r, 0, 0, ptr)
}

// ConvertColName 转换为结构体或切片  可转换的类型(*struct ,*[]struct, *[]*struct, *[]基本类型, *基本类型)
func ConvertColName(r *rawTable, colName string, ptr any) (err error) {
	colIndex := StringInIndex(colName, r.columnNames)
	return ConvertColIndex(r, colIndex, ptr)
}

// ConvertColIndex 转换为结构体或切片  可转换的类型(*struct ,*[]struct, *[]*struct, *[]基本类型, *基本类型)
func ConvertColIndex(r *rawTable, colIndex int, ptr any) (err error) {
	if colIndex < 0 || colIndex >= len(r.columnNames) {
		err = errors.New("err: column index out range")
		return err
	}
	return convert(r, 0, colIndex, ptr)
}

// convert 转换为结构体或切片  可转换的类型(*struct ,*[]struct, *[]*struct, *[]基本类型, *基本类型)
func convert(r *rawTable, rowIndex, colIndex int, ptr any) (err error) {
	if r.ColumnCount() <= 0 {
		return err
	}
	valueOf := reflect.ValueOf(ptr)
	if valueOf.Kind() != reflect.Pointer {
		err = errors.New("please pass in the pointer type")
		FuncLog(err)
		return err
	}
	valueOf = reflect.Indirect(valueOf)
	if valueOf.Kind() == reflect.Slice {
		// 处理 *[]*struct 类型
		sliceElemTypePtr := false
		sliceElemType := valueOf.Type().Elem()
		if sliceElemType.Kind() == reflect.Pointer {
			sliceElemTypePtr = true
			sliceElemType = sliceElemType.Elem()
		}
		temp := valueOf
		for i := 0; i < r.RowCount(); i++ {
			newVal := reflect.New(sliceElemType)
			names := r.columnNames
			elem := newVal.Elem()
			//基本类型
			if elem.Kind() != reflect.Struct || elem.Type().PkgPath() == "time" {
				err = setValue(&elem, *r.data[i][colIndex].(*interface{}))
				if err != nil {
					err = errors.New(fmt.Sprintf("type err: %v can‘t convert %s type ;%v", *r.data[i][0].(*interface{}), elem.Kind().String(), err))
					FuncLog(err)
					return err
				}
			} else {
				for j := range names {
					field := elem.FieldByName(Hump(names[j]))
					if !field.CanSet() {
						field = elem.FieldByName(PrefixUpper(names[j]))
					}
					err = setValue(&field, *r.data[i][j].(*interface{}))
					if err != nil {
						err = errors.New(fmt.Sprintf("field:[%s] type err: %v can‘t convert %s type ;%v", Hump(names[j]), *r.data[i][j].(*interface{}), field.Type().Name(), err))
						FuncLog(err)
						return err
					}
				}
			}
			if sliceElemTypePtr {
				temp = reflect.Append(temp, reflect.Indirect(newVal).Addr())
			} else {
				temp = reflect.Append(temp, reflect.Indirect(newVal))
			}
		}
		valueOf.Set(temp)
	} else if valueOf.Kind() != reflect.Struct || valueOf.Type().PkgPath() == "time" {
		err = setValue(&valueOf, *r.data[rowIndex][colIndex].(*interface{}))
		if err != nil {
			err = errors.New(fmt.Sprintf("type err: %v can‘t convert %s type ;%v", *r.data[0][0].(*interface{}), valueOf.Kind().String(), err))
			FuncLog(err)
			return err
		}
	} else {
		names := r.columnNames
		for i := range names {
			field := valueOf.FieldByName(Hump(names[i]))
			err = setValue(&field, *r.data[rowIndex][i].(*interface{}))
			if err != nil {
				err = errors.New(fmt.Sprintf("field:[%s] type err: %v can‘t convert %s type ;%v", Hump(names[i]), *r.data[0][i].(*interface{}), field.Type().Name(), err))
				FuncLog(err)
				return err
			}
		}
	}
	return err
}
